`timescale 1ns / 1ps
`include "def.v"

module CPU(
    input clk,
    input reset
    );
    // 流水线各级 PC Instr
    wire [31:0] NPC;
    wire [31:0] F_PC;
    wire [31:0] D_PC;
    wire [31:0] E_PC;
    wire [31:0] M_PC;
    wire [31:0] W_PC;
    wire [31:0] F_Instr;
    wire [31:0] D_Instr;
    wire [31:0] E_Instr;
    wire [31:0] M_Instr;
    wire [31:0] W_Instr;
    // 流水线各级寄存器写回序号（WrReg），缺省5'b0
    wire [4:0] E_GRFWrReg, M_GRFWrReg, W_GRFWrReg;
    // 流水线各级寄存器写回数据（WrData），缺省32'bz 
    wire [31:0] E_GRFWrData, M_GRFWrData, W_GRFWrData;

    // b型指令专用信号，代表是否跳转
    wire D_bjp_tag, E_bjp_tag, M_bjp_tag, W_bjp_tag;

    // 流水线冒险控制
    wire hazard;

    wire FD_REG_WrEn;
    wire PC_WrEn;    
    wire DE_REG_Flush;
    assign FD_REG_WrEn   = !hazard;
    assign PC_WrEn       = !hazard;    
    assign DE_REG_Flush  =  hazard;


    // 接收各级写回数据（WrData），不满足写回条件即为原始数据
    wire[31:0] D_rs_WDupdate, D_rt_WDupdate;
    wire[31:0] E_rs_WDupdate, E_rt_WDupdate;
    wire[31:0] M_rt_WDupdate;

//*************fetch******************fetch********************fetch************

    F_IFU _ifu(
        .clk(clk),
        .reset(reset),

        .PC_WrEn(PC_WrEn),
        .NPC(NPC),

        .Instr(F_Instr),
        .PC(F_PC)
    );

    // FD级寄存器
    FD_REG _fd_reg(
        .clk(clk),
        .reset(reset),
        .WrEn(FD_REG_WrEn),
        
        .F_PC(F_PC),
        .F_Instr(F_Instr),

        .D_PC(D_PC),
        .D_Instr(D_Instr)
    );

//*************decode******************decode********************decode*******

    // D级译码
    wire [4:0] D_rs_reg, D_rt_reg;
    wire [15:0] D_imm16;
    wire [25:0] D_imm26;
    wire [2:0] D_BJPOp;
    wire [2:0] D_NPCOp;
    wire D_EXTOp;
    wire [31:0] D_rs_data, D_rt_data;
    wire [31:0] D_ext32;
    wire D_branch,D_jump_reg,D_jump_addr; //hazard
    control D_CTRL(
        .Instr(D_Instr),
        
        .rs(D_rs_reg),
        .rt(D_rt_reg),
        .imm16(D_imm16),
        .imm26(D_imm26),
        .BJPOp(D_BJPOp),
        .NPCOp(D_NPCOp),
        .EXTOp(D_EXTOp),
        .calc_r(D_calc_r),  //hazard
        .branch(D_branch),    //hazard
        .jump_reg(D_jump_reg), //hazard
        .jump_addr(D_jump_addr) // hazard
    );

    D_GRF _grf(
        .clk(clk),
        .reset(reset),
        
        .RdReg1(D_rs_reg),
        .RdReg2(D_rt_reg),

        .PC(W_PC),
        .WrReg(W_GRFWrReg),
        .WD(W_GRFWrData),

        .RD1(D_rs_data),
        .RD2(D_rt_data)
    );

    D_EXT _ext(
        .imm16(D_imm16),
        .EXTOp(D_EXTOp),
        .ext32(D_ext32)
    );

    // WriteBack-------------WriteBack-------------WriteBack-------

    assign D_rs_WDupdate = (D_rs_reg == 0) ? 0 :
                           (D_rs_reg == E_GRFWrReg) ? E_GRFWrData :
                           (D_rs_reg == M_GRFWrReg) ? M_GRFWrData :
                           D_rs_data;
    
    assign D_rt_WDupdate = (D_rt_reg == 0) ? 0 :
                           (D_rt_reg == E_GRFWrReg) ? E_GRFWrData :
                           (D_rt_reg == M_GRFWrReg) ? M_GRFWrData :
                           D_rt_data;

    // WriteBack-------------WriteBack-------------WriteBack-------

    D_BJP _bjp(
        .BJPOp(D_BJPOp),
        .rsData(D_rs_WDupdate),
        .rtData(D_rt_WDupdate),
        .jump(D_bjp_tag)
    );

    D_NPC _npc(
        .NPCOp(D_NPCOp),
        .D_PC(D_PC),
        .F_PC(F_PC),
        .bjp_tag(D_bjp_tag),
        .imm26(D_imm26),
        .imm16(D_imm16),
        .rs(D_rs_WDupdate),

        .NPC(NPC)
    );


    wire [31:0] E_rs_data, E_rt_data;
    wire [31:0] E_ext32;
    // DE级寄存器
    DE_REG _de_reg(
        .clk(clk),
        .reset(reset),
        .flush(DE_REG_Flush),

        .D_PC(D_PC),
        .D_Instr(D_Instr),
        .D_ext32(D_ext32),
        .D_rs_data(D_rs_WDupdate),
        .D_rt_data(D_rt_WDupdate),
        .D_bjp_tag(D_bjp_tag),

        .E_PC(E_PC),
        .E_Instr(E_Instr),
        .E_rs_data(E_rs_data),
        .E_rt_data(E_rt_data),
        .E_ext32(E_ext32),
        .E_bjp_tag(E_bjp_tag)
    );

//*************execute******************execute********************execute*******

    // E级译码
    wire [4:0] E_rs_reg, E_rt_reg;
    wire [3:0] E_ALUOp;
    wire [1:0] E_ALUAtype, E_ALUBtype, E_GRFWDtype;
    wire E_calc_i,E_calc_r,E_load; //hazard
    control E_CTRL(
        .Instr(E_Instr),
        
        .rs(E_rs_reg),
        .rt(E_rt_reg),

        .ALUOp(E_ALUOp),
        .ALUAtype(E_ALUAtype),
        .ALUBtype(E_ALUBtype),
        .GRFWrReg(E_GRFWrReg),
        .GRFWDtype(E_GRFWDtype),
        .calc_i(E_calc_i), // hazard
        .calc_r(E_calc_r), // hazard
        .load(E_load)      // hazard
    );

    // WriteBack-------------WriteBack-------------WriteBack-------

    assign E_GRFWrData = (E_GRFWDtype == `WDtype_pc8 )? E_PC + 8 : 32'bz;


    assign E_rs_WDupdate = (E_rs_reg == 0) ? 0 :
                           (E_rs_reg == M_GRFWrReg) ? M_GRFWrData :
                           (E_rs_reg == W_GRFWrReg) ? W_GRFWrData :
                            E_rs_data;

    assign E_rt_WDupdate = (E_rt_reg == 0) ? 0 :
                           (E_rt_reg == M_GRFWrReg) ? M_GRFWrData :
                           (E_rt_reg == W_GRFWrReg) ? W_GRFWrData :
                            E_rt_data;

    wire [31:0] E_ALUA = (E_ALUAtype == `Atype_rs) ? E_rs_WDupdate : 0;
    
    wire [31:0] E_ALUB = (E_ALUBtype == `Btype_rt) ? E_rt_WDupdate :
                         (E_ALUBtype == `Btype_imm) ? E_ext32 : 0;
    // WriteBack-------------WriteBack-------------WriteBack-------

    wire [31:0] E_ALUAns;
    E_ALU _alu(
        .A(E_ALUA),
        .B(E_ALUB),
        .ALUOp(E_ALUOp),
        .C(E_ALUAns)
    );




    wire [31:0] M_ALUAns;       
    wire [31:0] M_rt_data;      
    // EM寄存器
    EM_REG _em_reg(
        .clk(clk),
        .reset(reset),

        .E_PC(E_PC),
        .E_Instr(E_Instr),
        .E_ALUAns(E_ALUAns),
        .E_rt_data(E_rt_WDupdate),
        .E_ext32(E_ext32),
        .E_bjp_tag(E_bjp_tag),

        .M_PC(M_PC),
        .M_Instr(M_Instr),
        .M_ALUAns(M_ALUAns),
        .M_rt_data(M_rt_data),
        .M_bjp_tag(M_bjp_tag)
    );

//*************memory******************memory********************memory*******

    // M级译码
    wire [4:0] M_rt_reg;
    wire M_DMWrEn;
    wire [1:0] M_GRFWDtype;
    wire M_load;  //hazard
    control M_CTRL(
        .Instr(M_Instr),

        .load(M_load),  // hazard
        .rt(M_rt_reg),
        .DMWrEn(M_DMWrEn),
        .GRFWrReg(M_GRFWrReg),
        .GRFWDtype(M_GRFWDtype)
    );

    // WriteBack-------------WriteBack-------------WriteBack-------
    assign M_rt_WDupdate = (M_rt_reg == 0) ? 0 :
                           (M_rt_reg == W_GRFWrReg) ? W_GRFWrData :
                           M_rt_data;
    
    assign M_GRFWrData = M_GRFWDtype == `WDtype_pc8 ? M_PC + 8 :
                     M_GRFWDtype == `WDtype_aluans ? M_ALUAns :
                     32'bz;
     // WriteBack-------------WriteBack-------------WriteBack-------
     
    wire [31:0] M_DMRD;
    M_DM _dm(
        .clk(clk),
        .reset(reset),

        .Addr(M_ALUAns),
        .WD(M_rt_WDupdate),
        .DMWrEn(M_DMWrEn),
        
        .RD(M_DMRD)
    );


    wire [31:0] W_ALUAns;      
    wire [31:0] W_DMRD;         
    // MW寄存器
    MW_REG _mw_reg(
        .clk(clk),
        .reset(reset),

        .M_PC(M_PC),
        .M_Instr(M_Instr),
        .M_ALUAns(M_ALUAns),
        .M_DMRD(M_DMRD),
        .M_bjp_tag(M_bjp_tag),

        .W_PC(W_PC),
        .W_Instr(W_Instr),
        .W_ALUAns(W_ALUAns),
        .W_DMRD(W_DMRD),
        .W_bjp_tag(W_bjp_tag)
    );

    //W级译码
    wire [1:0] w_GRFWDtype;
    control W_CTRL(
        .Instr(W_Instr),
        
        .GRFWrReg(W_GRFWrReg),
        .GRFWDtype(w_GRFWDtype)
    );
    // WriteBack-------------WriteBack-------------WriteBack-------
    assign W_GRFWrData = w_GRFWDtype == `WDtype_pc8 ? W_PC + 8 :
                     w_GRFWDtype == `WDtype_aluans ? W_ALUAns :
                     w_GRFWDtype == `WDtype_dmrd ? W_DMRD :
                     32'dz;


//**************hazard****************hazard**************hazard***************
    wire DrsE_hazard,DrtE_hazard,DrsM_hazard,DrtM_hazard;
    // hazard : load -> cal , cal -> jump b/r (D depend on E and DE at one time)
    assign DrsE_hazard = (E_GRFWrReg != D_rs_reg || D_rs_reg == 0)?1'b0:
                       (D_jump_addr)?1'b0:  
                       (E_load) ?1'b1:
                       (E_calc_i | E_calc_r) && (D_branch | D_jump_reg);   
    assign DrtE_hazard = (E_GRFWrReg != D_rt_reg || D_rt_reg == 0)?1'b0:
                         (E_load && (D_branch | D_calc_r))?1'b1:
                         ((E_calc_i | E_calc_r) && D_branch);  

    // hazard : load -> jump b/r (D depend on M and DM at one time)
    assign DrsM_hazard = (M_GRFWrReg != D_rs_reg && D_rs_reg == 0)?1'b0:
                       (M_load) && (D_branch | D_jump_reg);
    assign DrtM_hazard = (M_GRFWrReg != D_rt_reg && D_rt_reg == 0)?1'b0:
                       (M_load && D_branch);
    
    assign hazard = DrsE_hazard | DrtE_hazard | DrsM_hazard | DrtM_hazard ;
endmodule
